package org.invigorare.auctions;

import java.util.*;

import org.invigorare.Utils;
import org.invigorare.auctions.comm.DbSyncResult;
import org.invigorare.auctions.comm.DbSynchronizer;
import org.invigorare.auctions.comm.InboundSyncResult;
import org.invigorare.auctions.comm.DbSyncResult.RowResult;
import org.invigorare.auctions.comm.EntrySyncResult.Status;
import org.invigorare.auctions.comm.InboundSyncResult.InboundStatus;
import org.invigorare.auctions.data.AppSettings;

import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;

public class SyncMenu extends Activity {

	AppSettings settings;
	enum Mode {VERIFY, SYNC};
	Mode mode;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.sync);

        settings = new AppSettings(getApplicationContext());
        mode = (settings.settingsVerified()) ? Mode.VERIFY : Mode.SYNC;
        
        associateFieldVars();
        populateFields();
        setupButtons();
	}

	EditText webserviceUrl, employeeId;
	TextView marketName, employeeInfo;
	TextView feeUnload, feeClean, feeWeigh, feeLoad, feePack, feeStitch, commission2agent, commission2market;
	Button syncButton, confirmButton;
	
	LinearLayout syncResultsLayout, outErrorsLayout; 
	TextView textSyncResults;
	TableLayout tableOutErrors;
	
	/**
     * Finds the Views of each form field in this form and saves them as variables for easier access.
     */
    private void associateFieldVars() {
    	
    	/* Metadata */
    	webserviceUrl = (EditText) findViewById(R.id.sync_serverUrl);
    	employeeId = (EditText) findViewById(R.id.sync_empId);
    	marketName = (TextView) findViewById(R.id.sync_mandiName);
    	employeeInfo = (TextView) findViewById(R.id.sync_empName);
    	
    	/* Fees & Commissions */
    	feeUnload = (TextView) findViewById(R.id.sync_fees_unload);
    	feeClean = (TextView) findViewById(R.id.sync_fees_clean);
    	feeWeigh = (TextView) findViewById(R.id.sync_fees_weigh);
    	feeLoad = (TextView) findViewById(R.id.sync_fees_load);
    	feePack = (TextView) findViewById(R.id.sync_fees_pack);
    	feeStitch = (TextView) findViewById(R.id.sync_fees_stitch);
    	commission2agent = (TextView) findViewById(R.id.sync_fees_commission_to_agent);
    	commission2market = (TextView) findViewById(R.id.sync_fees_commission_to_market);
    	
        /* Sync buttons */
    	syncButton = (Button) findViewById(R.id.button_do_sync);
    	confirmButton = (Button) findViewById(R.id.button_sync_settingsOk);
    	
    	/* Sync Results & Errors */
    	syncResultsLayout = (LinearLayout) findViewById(R.id.sync_layout_results);
    	textSyncResults = (TextView) findViewById(R.id.sync_results);
    	
    	outErrorsLayout = (LinearLayout) findViewById(R.id.sync_layout_outErrors);
    	tableOutErrors = (TableLayout) findViewById(R.id.sync_table_outErrors);
    }
	
    private void populateFields() {
    	
    	Context c = getApplicationContext();
    	
    	webserviceUrl.setText(settings.getWebserviceURL());
    	employeeId.setText(settings.getEmployeeId());
    	marketName.setText(settings.getMarketName());
    	employeeInfo.setText(settings.getEmployeeName());
    	
    	Map<String, Float> fees = settings.getCommissionsAndFees();
    	Utils.setFeeRateText(c, feeUnload, fees.get(AppSettings.FEE_UNLOAD));
    	Utils.setFeeRateText(c, feeClean, fees.get(AppSettings.FEE_CLEAN));
    	Utils.setFeeRateText(c, feeWeigh, fees.get(AppSettings.FEE_WEIGH));
    	Utils.setFeeRateText(c, feeLoad, fees.get(AppSettings.FEE_LOAD));
    	Utils.setFeeRateText(c, feePack, fees.get(AppSettings.FEE_PACK));
    	Utils.setFeeRateText(c, feeStitch, fees.get(AppSettings.FEE_STITCH));
    	
    	Utils.setCommissionRateText(c, commission2agent, fees.get(AppSettings.COMMISSION_AGENT));
    	Utils.setCommissionRateText(c, commission2market, fees.get(AppSettings.COMMISSION_MANDI));
    }
    
    private void setupButtons() {
    	syncButton.setOnClickListener(new ClickListener_SynchronizeDatabases());
    	confirmButton.setOnClickListener(new ClickListener_GoBack());
    	
    	webserviceUrl.addTextChangedListener(new ChangeListener_SetVerifyMode());
    	employeeId.addTextChangedListener(new ChangeListener_SetVerifyMode());
    	
    	updateButtons();
    }
    
    /** The OK Button will be visible and enabled ONLY IF 
     * "FIRST_LAUNCH" in settings is FALSE, i.e. database has successfully sync'd at least once,
     * and employee Id has been validated.
     * 
     * The Sync button will display "Verify Settings" on first launch, or if settings have been changed, until
     * a successful inbound sync is achieved, after which time it will display "Manual Sync".
     */
    private void updateButtons() {
    	if (settings.settingsVerified() || mode == Mode.VERIFY) {
    		syncButton.setText(R.string.sync_verifySettings);
    		confirmButton.setVisibility(View.INVISIBLE);
    	} else {
    		syncButton.setText(R.string.sync_manualSync);
    		confirmButton.setVisibility(View.VISIBLE);
    	}
    }
    
    protected class SyncResults {
    	final InboundSyncResult inboundResult;
    	final DbSyncResult outboundResult;
    	
    	public SyncResults(InboundSyncResult inboundResult, DbSyncResult outboundResult) {
    		this.inboundResult = inboundResult;
    		this.outboundResult = outboundResult;
    	}
    }
    
    /**
     * Reads the employeeId and webserverUrl entered in the text fields, and adjusts
     * the current 'mode' to VERIFY_SETTINGS if necessary.
     * 
     * Performs a complete DB synchronization procedure.
     * 
     * @return the success or failure report of the inbound sync.
     */
    protected SyncResults doSync() {
    	
    	/* If anything changed, need to VERIFY settings */
		if (formValuesChanged(true)) {
			mode = Mode.VERIFY;
		}
		
		/* Perform synchronization and show a formatted toast */
		DbSynchronizer sync = new DbSynchronizer(SyncMenu.this);
		InboundSyncResult inboundResult = sync.syncDbInbound();
		DbSyncResult outboundResult = sync.syncDbOutbound();
		
		/* Return the success or failure report of the inbound and outbound sync */
		return new SyncResults(inboundResult, outboundResult);
    }
    
    protected void displaySyncResults(SyncResults syncResults) {
    	
    	displayGeneralSyncResult(syncResults);
    	
    	displayOutboundSyncErrors(syncResults.outboundResult);
    }
    
    private void displayGeneralSyncResult(SyncResults sync) {
    	
    	syncResultsLayout.setVisibility(View.VISIBLE);

    	Context context = getApplicationContext();
    	String resultText = context.getString(R.string.sync_result_format, 
    			sync.inboundResult.toString(getApplicationContext()),
    			sync.outboundResult.toString()
    			);
    	
    	textSyncResults.setText(resultText);
    	
    }
    
    /**
     * Display those Rows which were not SUCCESSful or NO_CONNECTION and allow the user to edit those entries.
     * @param outboundResult
     */
    private void displayOutboundSyncErrors(DbSyncResult outboundResult) {
		    	
	    if (outboundResult.allSuccessfulOrNoConn()) {
	    	outErrorsLayout.setVisibility(View.GONE);
	    } else {
	    	
	    	/* Remove all except header row */
	    	TableRow headerRow = (TableRow) tableOutErrors.getChildAt(0); // must exist in the .xml layout
	    	tableOutErrors.removeAllViews();
	    	tableOutErrors.addView(headerRow);

	    	/* Populate new rows */
	    	List<RowResult> failedRows = outboundResult.getRowResultsWithOUTStatuses(Status.SUCCESS, Status.NO_CONNECTION);
	    	
	    	for (RowResult r : failedRows) {
	    		tableOutErrors.addView(createOneErrorTableRow(r));
	    	}
	    	
	    	outErrorsLayout.setVisibility(View.VISIBLE);
	    }
    }

    private TableRow createOneErrorTableRow(RowResult r) {
    	Context context = getApplicationContext();
    	
    	TableRow row = new TableRow(context);
    	
    	TextView rowId = new TextView(context);
    	TextView errorDesc = new TextView(context);
    	Button viewButton = new Button(context);
    	
    	rowId.setText(String.valueOf(r.rowId)); rowId.setGravity(Gravity.CENTER_HORIZONTAL);
    	errorDesc.setText(r.result.getObjectAsString());
    	viewButton.setText(R.string.sync_outError_ViewSale);

    	row.addView(rowId);
    	row.addView(errorDesc);
    	row.addView(viewButton);
    	
    	viewButton.setEnabled(false); // TODO: enable Row Edit buttons
    	
    	return row;
    }
    
    private boolean formValuesChanged(boolean saveChanges) {
    	/* Read the latest form values */
    	String empId = employeeId.getText().toString();
		String webUrl = webserviceUrl.getText().toString();
		
		/* Compare them to what's stored */
		if (!empId.equals(settings.getEmployeeId()) || !webUrl.equals(settings.getWebserviceURL())) {
			if (saveChanges) {
				Log.i("SYNCMENU.SAVE", "saved");
				settings.saveUserSettings(webUrl, empId);
			}
			Log.i("SYNCMENU", "form field changed");
			return true;
		} else {
			return false;
		}
    }
    
    private class ClickListener_SynchronizeDatabases implements View.OnClickListener {
    	
    	public void onClick(View v) {
    		
    		SyncResults results = doSync();
			InboundSyncResult inboundResult = results.inboundResult;
			
    		/* If we were in VERIFY_SETTINGS mode and did a successful sync, move to regular SYNC mode */
    		if (mode == Mode.VERIFY && inboundResult.getOverallStatus() == InboundStatus.NORMAL && inboundResult.settingsSuccessful()) {
    			mode = Mode.SYNC;
			}
			
			populateFields();
			updateButtons();
			displaySyncResults(results);
    	}
    }
    
    private class ClickListener_GoBack implements View.OnClickListener {

		public void onClick(View v) {
			SyncMenu.this.finish();
		}
    	
    }
    
    private class ChangeListener_SetVerifyMode implements TextWatcher {


		public void afterTextChanged(Editable s) {
			// TODO Auto-generated method stub
			
		}

		public void beforeTextChanged(CharSequence s, int start, int count, int after) {
			// TODO Auto-generated method stub
			
		}

		public void onTextChanged(CharSequence s, int start, int before, int count) {
			/* If anything changed, need to verify settings (but don't save these changes here, just check) */
			if (formValuesChanged(false)) {
				mode = Mode.VERIFY;
			} else {
				mode = Mode.SYNC;
			}
			updateButtons();
		}
    	
    }
    
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
    	//ignore orientation change
    	super.onConfigurationChanged(newConfig);
    }
}
